package com.macro.mall.tiny.modules.foster.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.macro.mall.tiny.common.api.CommonResult;
import com.macro.mall.tiny.common.utils.JwtTokenUtil;
import com.macro.mall.tiny.constants.RedisConstant;
import com.macro.mall.tiny.modules.foster.mapper.PetMapper;
import com.macro.mall.tiny.modules.foster.mapper.UserMapper;
import com.macro.mall.tiny.modules.foster.mapper.UserRoleMapper;
import com.macro.mall.tiny.modules.foster.mapper.UserRoleRelationMapper;
import com.macro.mall.tiny.modules.foster.model.Pet;
import com.macro.mall.tiny.modules.foster.model.User;
import com.macro.mall.tiny.modules.foster.model.UserRole;
import com.macro.mall.tiny.modules.foster.model.UserRoleRelation;
import com.macro.mall.tiny.modules.foster.model.domain.AdminUserDetails;
import com.macro.mall.tiny.modules.foster.model.dto.AuthCodeDTO;
import com.macro.mall.tiny.modules.foster.model.dto.LoginDTO;
import com.macro.mall.tiny.modules.foster.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;

import static com.baomidou.mybatisplus.core.toolkit.IdWorker.getId;


/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zhuyi
 * @since 2024-03-26
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserRoleMapper userRoleMapper;
    @Autowired
    private UserRoleRelationMapper userRoleRelationMapper;
    @Autowired
    private JwtTokenUtil jwtTokenUtil;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private PetMapper petMapper;


    /**
     * 通过用户名获取用户信息。
     * 这个方法用于根据提供的用户名查询用户，并返回包含用户角色信息的AdminUserDetails对象。
     *
     * @param username 用户名，用于查询用户。
     * @return 如果找到对应的用户，返回包含用户角色信息的AdminUserDetails对象；否则返回null。
     */
    @Override
    public AdminUserDetails getUserByUsername(String username) {
        // 使用QueryWrapper构建查询条件，查询指定用户名的用户
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("username", username);
        User user = userMapper.selectOne(wrapper);

        // 如果未找到对应的用户，则直接返回null
        if (user == null) {
            return null;
        }

        // 调用方法获取用户角色信息，并将其添加到授权列表中
        String roles = getRoles(user.getId());
        ArrayList<String> authList = new ArrayList<>();
        authList.add(roles);

        // 创建并返回AdminUserDetails对象，包含用户认证和授权信息
        AdminUserDetails adminUserDetails = new AdminUserDetails(user.getUsername(), user.getPassword(), authList);
        return adminUserDetails;
    }


    /**
     * 登录
     *
     * @param loginDTO
     * @return
     */
    @Override
    public String login(LoginDTO loginDTO) {
        String token = null;
        try {
            UserDetails userDetails = getUserByUsername(loginDTO.getUsername());
            if (userDetails == null) {
                return token;
            }
            if (!passwordEncoder.matches(loginDTO.getPassword(), userDetails.getPassword())) {
                throw new BadCredentialsException("密码不正确");
            }
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
        }
        LOGGER.info("{}用户登录成功", loginDTO.getUsername());
        return token;
    }

    /**
     * 获取权限角色
     *
     * @param userId
     * @return
     */
    @Override
    public String getRoles(Long userId) {
        QueryWrapper<UserRoleRelation> relationQueryWrapperwrapper = new QueryWrapper<>();
        relationQueryWrapperwrapper
                .eq("user_id", userId);
        UserRoleRelation userRoleRelation = userRoleRelationMapper.selectOne(relationQueryWrapperwrapper);
        if (userRoleRelation == null) {
            log.debug("该用户没设置角色");
            return null;
        }
        QueryWrapper<UserRole> userRoleQueryWrapper = new QueryWrapper<>();
        userRoleQueryWrapper
                .eq("id", userRoleRelation.getRoleId());
        UserRole userRole = userRoleMapper.selectOne(userRoleQueryWrapper);
        return userRole.getName();
    }

    /**
     * 校验验证码
     *
     * @param authCodeDTO
     * @return
     */
    @Override
    public CommonResult checkAuthCode(AuthCodeDTO authCodeDTO) {
        String getCode = (String) redisTemplate.opsForValue().get(RedisConstant.AUTH_CODE_KEY + authCodeDTO.getEmail());
        if (getCode == null) {
            //验证码失效
            return CommonResult.authCodeNotExist();
        }
        if (!getCode.equals(authCodeDTO.getAuthCode())) {
            //校验失败
            return CommonResult.codeCheckfailed();
        }
        redisTemplate.delete(RedisConstant.AUTH_CODE_KEY + authCodeDTO.getEmail());
        LOGGER.info("验证码校验成功");
        return CommonResult.success("校验通过");
    }

    /**
     * 用户注册功能实现。
     * 对传入的用户对象进行注册流程处理，包括检查用户名和手机号是否唯一，密码加密，设置默认昵称，
     * 分配角色等操作。
     *
     * @param user 用户对象，包含用户名、密码、手机号等注册信息。
     * @return 返回注册结果，成功返回通用成功结果，失败则返回相应的错误信息。
     */
    @Override
    public CommonResult register(User user) {
        // 检查用户名是否已经存在
        User usernameCheck = userMapper.selectUserByUsername(user.getUsername());
        if (usernameCheck != null) {
            return CommonResult.userNameHasExist();
        }

        // 检查手机号是否已经存在
        QueryWrapper<User> wrapper2 = new QueryWrapper<>();
        wrapper2.eq("phone", user.getPhone());
        User emailCheck = userMapper.selectOne(wrapper2);
        if (emailCheck != null) {
            return CommonResult.phoneHasExist();
        }

        // 设置用户ID、加密密码、设置默认昵称、设置用户角色等
        user.setId(getId());
        String password = new BCryptPasswordEncoder().encode(user.getPassword());
        user.setPassword(password);
        if (user.getNickName() == null || user.getNickName().equals("")) {
            user.setNickName(defaultNickName(user.getPhone()));
        }
        user.setIsAdmin(0);
        userMapper.insert(user);

        // 分配角色
        UserRoleRelation userRoleRelation = new UserRoleRelation();
        userRoleRelation.setId(getId());
        userRoleRelation.setUserId(user.getId());
        userRoleRelationMapper.insert(userRoleRelation);

        // 记录日志并返回注册成功信息
        LOGGER.info("{}用户注册成功", user.getUsername());
        return CommonResult.success("注册成功");
    }


    /**
     * 根据id获取用户
     *
     * @param id
     * @return
     */
    @Override
    public CommonResult getUserById(Long id) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                .eq("id", id)
                .eq("is_admin", 0);
        User user = userMapper.selectOne(wrapper);
        if (user == null) {
            return CommonResult.userNotExist();
        }
        LOGGER.info("获取用户{}成功", user.getUsername());
        return CommonResult.success(user);
    }

    /**
     * 删除用户及其相关角色和宠物信息
     *
     * @param id 用户ID
     * @return 返回操作结果，成功返回成功信息，用户不存在返回用户不存在信息
     */
    @Override
    public CommonResult removeUserById(Long id) {
        // 根据ID查询用户，确保用户存在
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                .eq("id", id)
                .eq("is_admin", 0);
        User user = userMapper.selectOne(wrapper);
        if (user == null) {
            // 用户不存在时返回相应信息
            return CommonResult.userNotExist();
        }
        // 删除用户
        userMapper.deleteById(user.getId());

        // 删除用户角色关系
        QueryWrapper<UserRoleRelation> relationWrapper = new QueryWrapper<>();
        relationWrapper
                .eq("user_id", id);
        UserRoleRelation userRoleRelation = userRoleRelationMapper.selectOne(relationWrapper);
        userRoleRelationMapper.deleteById(userRoleRelation);

        // 删除用户拥有的宠物
        QueryWrapper<Pet> petWrapper = new QueryWrapper<>();
        petWrapper
                .eq("user_id", user.getId());
        petMapper.delete(petWrapper);

        LOGGER.info("用户已删除：{}", user.getUsername());

        // 删除成功后返回成功信息
        return CommonResult.success("删除成功");
    }


    /**
     * 修改用户信息
     *
     * @param user 包含更新后用户信息的对象，其中必须包含用户ID
     * @return 返回操作结果，成功返回成功信息，失败返回错误信息
     */
    @Override
    public CommonResult updateUserById(User user) {
        // 构建查询条件，检查待更新用户是否存在且不是管理员
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper
                .eq("id", user.getId())
                .eq("is_admin", 0);
        User userCheck = userMapper.selectOne(wrapper);
        // 如果用户不存在，则返回用户不存在的结果
        if (userCheck == null) {
            return CommonResult.userNotExist();
        }

        // 设置更新用户的ID，防止更新其他字段
        user.setId(userCheck.getId());

        // 检查要更新的用户名是否已被其他用户使用
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper
                .eq("username", user.getUsername())
                .ne("id", user.getId());
        User user1 = userMapper.selectOne(queryWrapper);
        // 如果用户名已存在，则返回用户名已存在的结果
        if (user1 != null) {
            return CommonResult.userNameHasExist();
        }

        // 检查要更新的手机号是否已被其他用户使用
        User user2 = userMapper.selectUserByPhoneAndNotMe(user.getPhone(),user.getId());
        // 如果手机号已存在，则返回手机号已存在的结果
        if (user2 != null) {
            return CommonResult.phoneHasExist();
        }

        // 如果用户未提供新密码，则使用原密码；否则，对新密码进行加密处理
        if (StringUtils.isBlank(user.getPassword())) {
            user.setPassword(userCheck.getPassword());
        } else {
            String password = new BCryptPasswordEncoder().encode(user.getPassword());
            user.setPassword(password);
        }

        // 执行用户信息更新
        userMapper.updateById(user);
        LOGGER.info("修改用户{}成功", user.getUsername());
        // 返回操作成功的结果
        return CommonResult.success("修改成功");
    }


    /**
     * 忘记密码功能实现。允许用户重设密码。
     *
     * @param loginDTO 包含用户登录信息的数据传输对象，通常包括用户名和新密码。
     * @return 返回一个通用结果对象，表明密码重设是否成功。如果用户不存在，返回用户不存在的信息。
     */
    @Override
    public CommonResult updatePassword(LoginDTO loginDTO) {
        // 根据用户名查询用户信息
        User userCheck = userMapper.selectUserByUsername(loginDTO.getUsername());
        // 如果用户不存在，则返回用户不存在的错误信息
        if (userCheck == null) {
            return CommonResult.userNotExist();
        }
        // 新密码经过BCrypt加密算法编码
        String newPassword = loginDTO.getPassword();
        String password = new BCryptPasswordEncoder().encode(newPassword);
        userCheck.setPassword(password);
        // 更新数据库中的用户密码
        userMapper.updateById(userCheck);
        // 记录密码成功修改的日志
        LOGGER.info("{}用户修改密码成功", loginDTO.getUsername());
        // 返回密码修改成功的消息
        return CommonResult.success("修改密码成功");
    }


    /**
     * 生成默认昵称
     *
     * @param phone 用户的电话号码
     * @return 返回基于电话号码生成的默认昵称
     */
    public String defaultNickName(String phone) {
        // 定义前缀，用于生成默认昵称
        String key = "foster_";
        // 拼接电话号码和前缀生成默认昵称
        String result = key + phone;
        return result;
    }


}
